<script setup lang="ts">
import {defineOptions, nextTick, onMounted, onUnmounted, ref, useSlots, watch} from 'vue';
import {ElCard, ElPagination, ElTable, ElTableColumn} from 'element-plus'
import {debounce} from 'lodash';
import Sortable from 'sortablejs';
import {useDevMethods} from '@/utils/methodProvider';
import {devDeepMerge, useConfig} from '@/index';
import {devSearchTableProps} from './searchTable';
import {useElementSize} from '@vueuse/core';
import DevTableColumn from "../components/DevTableColumn.vue";

const props = defineProps(devSearchTableProps)
const slots = useSlots()

const methods = useDevMethods();

const emit = defineEmits(['check-change', 'sort-table-change','update:query','result']);
const tableData = ref<object[]>([]);

//** ********************** 勾选项触发逻辑 *****************************/

/**
 * 历史页勾选信息
 */
const historySelection = ref<any>({});

/**
 * 勾选项发生改变时触发
 */
const handleSelectionChange = debounce((val: any[]) => {
  if (props.isPagination) {
    historySelection.value[currentPage.value] = val;
    nextTick(() => {
      const allSelectData: any[] = [];
      let allSelectId: any[] = [];
      Object.keys(historySelection.value).forEach((currentKey: any) => {
        historySelection.value[currentKey].forEach((item: any) => {
          if (allSelectId.indexOf(item[defaultTableProps.value.rowKey || 'id']) === -1) {
            allSelectData.push(item);
          }
          allSelectId = [...allSelectId, item[defaultTableProps.value.rowKey || 'id']];
        });
      });
      emit('check-change', {allSelectData, allSelectId});
    });
  }else{
    const allSelectId: any[] = val.map((item => item[defaultTableProps.value.rowKey || 'id']));
    emit('check-change', {allSelectData:val, allSelectId});
  }
}, 200);

/**
 * DevSearchTable组件开启勾选功能（openSelection）后，主动清除已勾选内容
 */
const clearAllSelection = () => {
  historySelection.value = {};
  devElTableRef.value!.clearSelection();
};


/**
 * 回显勾选项方法
 */
const toggleSelection = (rows?: number[]) => {
  if (rows) {
    rows.forEach((row) => {
      // TODO: improvement typing when refactor table
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      devElTableRef.value!.toggleRowSelection(tableData.value[row], undefined);
    });
  } else {
    devElTableRef.value!.clearSelection();
  }
};

//** ************************* 拖拽排序逻辑 *********************************/
const copyTableDataStore = ref<any[]>([]);

const initSortable = () => {
  const el = devElTableRef.value?.$el.querySelector('.el-table__body-wrapper tbody');
  Sortable.create(<HTMLElement>el, {
    onEnd: ({newIndex, oldIndex}) => {
      if (newIndex !== oldIndex) {
        let copyData = JSON.parse(JSON.stringify(tableData.value));
        copyData.splice(newIndex, 0, copyData.splice(oldIndex, 1)[0]);
        tableData.value = copyData;
        emit('sort-table-change', copyData)
      }
    },
    ...props.sortableOptions
  });
}

//** ************************* 组件默认绑定方法 *********************************/

/**
 *  默认使用el-table的方法信息(也可以用户自定义)
 */
const defaultTableProps = ref<Record<string, any>>({});
watch(() => props.elTableAttrs, (value) => {
  defaultTableProps.value = devDeepMerge({onSelectionChange: handleSelectionChange, ...useConfig().elTableProps}, value);
}, {deep: true, immediate: true})

/**
 * 跳转界面默认绑定的参数
 */
const defaultPaginationProps = devDeepMerge(useConfig().devSearchTablePaginationProps,props.elPaginationAttrs);

//** ************************** 获取列表数据 ***********************/
const loading = ref<boolean>(false);
const totalNum = ref<number>(0);// 总条数
const currentPage: any = ref(1); // 当前页码
const pageSize: any = ref(defaultPaginationProps.pageSizes[0] || 10); // 每页条数

const devElTableRef: any = ref<InstanceType<typeof ElTable>>();

/**
 * 同步页码/每页条数
 */
const syncPage = (val: any) => {
  currentPage.value = val[props.currentPageKey || 'currentPage'] || currentPage.value;
  pageSize.value = val[props.pageSizeKey || 'pageSize'] || pageSize.value;
  emit('update:query', val);
};

/**
 * 每页条数改变
 */
const sizeChange = (val: number) => {
  pageSize.value = val;
  emit('update:query', { ...props.query,[props.pageSizeKey || 'pageSize']:val || {}});
  nextTick(()=>{
    getTableList();
  })
};

/**
 * 当前页码改变
 */
const currentChange = (val: number) => {
  currentPage.value = val;
  emit('update:query', { ...props.query,[props.currentPageKey || 'currentPage']:val || {}});
  nextTick(()=>{
    getTableList();
  })
};

/**
 * 切换页码后,界面滚动到顶部
 */
const scrollToTop = () => {
  if(!props.containerName) return;
  const container = document.querySelector(props.containerName);
  if(!container) return;
  container.scrollTo({
    top: props.scrollTopNumber,
    behavior: props.scrollTopBehavior
  });
};

/**
 * 判断是否开启拖拽
 */
const isOpenSortable = () => {
  // 如果开启了拖拽排序
  if (props.openSortable) {
    initSortable();
  }
};

/**
 * 更新表头吸顶视图
 */
const updateTableHeaderView = () => {
  if (!headerIsIntersecting.value && tableIsIntersecting.value && !loading.value && props.openHeaderSticky) {
    const headerWrapper = devElTableRef.value?.$el.querySelector('.el-table__header-wrapper');
    if (!headerWrapper) return
    headerWrapper.style.position = 'fixed';
    headerWrapper.style.zIndex = props.headerStickyZIndex;
    headerWrapper.style.top = props.headerStickyTop;
    headerWrapper.style.maxWidth = `${tableWidth.value}px`;
  }
}
/**
 * 延迟更新表头吸顶视图
 */
const delayUpdateTableHeaderView = debounce(() => {
  updateTableHeaderView();
}, props.headerStickyRefreshSpeed);

const tableWidth = useElementSize(devElTableRef).width;
// 监听表格宽度变化
watch(() => tableWidth.value, (value) => {
  if (props.headerStickyRefreshSpeed) {
    delayUpdateTableHeaderView();
  } else {
    updateTableHeaderView();
  }
})

watch(() => [props.openHeaderSticky, props.openRollerSticky], () => {
  getDomInfo();
});

/**
 * 判断是否开启吸顶或吸底
 */
const isSticky = ()=>{
  // 如果开启了表头吸顶/或底部滚动条吸底
  if (props.openHeaderSticky || props.openRollerSticky) {
    getDomInfo();
  }
}

/**
 * DevSearchTable组件中获取列表数据
 */
const getTableList = async () => {
  // 增加校验逻辑
  if(props.isFormTable && props.devSearchFormRef){
    const val = await props.devSearchFormRef.formValidate();
    if(!val)return;
  }
  if (props.api && props.openRequest && !loading.value) {
    loading.value = true;
    const params = props.isPagination? {
      [props.currentPageKey || 'currentPage']: currentPage.value,
      [props.pageSizeKey || 'pageSize']: pageSize.value
    }:{};
    // 如果开启了滚动到顶部
    if (props.openScrollTop) {
      scrollToTop();
    }
    // 同步页码/每页条数
    if(props.isPagination) syncPage({...params, ...props.query || {}});
    props.api({...params, ...props.query || {}}).then((res: any) => {
      emit('result',res);
      const getTableField = props.devSearchTableFieldsFn ?? methods.devSearchTableFieldsFn;
      const data_ = getTableField(res, props, 'data') || [];
      const total_ = props.isPagination?getTableField(res, props, 'total'):0 || 0;
      copyTableDataStore.value = tableData.value = data_;
      totalNum.value = total_;
      loading.value = false;
      // 如果当前使用了选择页存在历史勾选信息,需要进行回显
      if (props.openSelection && props.isPagination) {
        let allRowKey: any[] = [];
        Object.keys(historySelection.value).forEach((currentKey: any) => {
          historySelection.value[currentKey].forEach((item: any) => {
            allRowKey = [...allRowKey, item[defaultTableProps.value.rowKey || 'id']];
          });
        });
        const allIndex: number[] = [];
        tableData.value.forEach((item:any, index) => {
          if (allRowKey.includes(item[defaultTableProps.value.rowKey || 'id'])) {
            allIndex.push(index);
          }
        });
        nextTick(() => {
          toggleSelection(allIndex);
        });
      }
      isOpenSortable();
      isSticky();
    }).catch(() => {
      loading.value = false;
    });
  }else{
    if(props.data && props.data.length > 0)return;
    if (!props.api) console.error('请传入api方法');
    if (!props.openRequest && !!props.api) console.error('当前未开启请求数据');
    if (loading.value) console.error('当前正在请求数据中');
  }
};

/**
 * DevSearchTable组件中重置列表数据
 */
const resetTableList = () => {
  currentPage.value = 1;
  pageSize.value = defaultPaginationProps.pageSizes[0];
  const params = {
    [props.currentPageKey || 'currentPage']:currentPage.value,
    [props.pageSizeKey || 'pageSize']:pageSize.value
  };
  // 同步页码/每页条数
  syncPage({ ...props.query || {},...params});
  getTableList();
};

//** ********************* 表头吸顶逻辑/滚动条吸底逻辑 ***********************/
const anchorPointRef = ref<any>(null);// 表头
const rollerAnchorPoint = ref<any>(null); // 底部滚动条吸底
const tableStyle = ref({});
const tableIsIntersecting = ref<boolean>(false);
const tableIsIntersectingHeight = ref<number>(0);
const headerIsIntersecting = ref<boolean>(false);
const bottomIsIntersecting = ref<boolean>(false);

/**
 * 重置样式
 */
const resetStyles = () => {
  const headerWrapper = devElTableRef.value?.$el.querySelector('.el-table__header-wrapper');
  if (headerWrapper) {
    anchorPointRef.value.style.height = '';
    headerWrapper.style.position = '';
    headerWrapper.style.zIndex = '';
    headerWrapper.style.top = '';
    headerWrapper.style.maxWidth = '100%';
  }
  const rollerWrapper = devElTableRef.value?.$el.querySelector('.el-scrollbar__thumb');
  if (rollerWrapper) {
    rollerWrapper.style.position = '';
    rollerWrapper.style.bottom = '';
  }
}

/**
 * 设置样式
 */
const setStyles = () => {
  //首先需要判断整个Table是否在可视区域内 如果不在则赋予默认值
  if (tableIsIntersecting.value) {
    // 表头吸顶逻辑
    const headerWrapper = devElTableRef.value?.$el.querySelector('.el-table__header-wrapper');
    if (!headerIsIntersecting.value && props.openHeaderSticky && headerWrapper && (tableIsIntersectingHeight.value > headerWrapper.offsetHeight * 2)) {
      if (headerWrapper.style.position !== 'fixed') { // 仅在状态变化时修改样式
        headerWrapper.style.position = 'fixed';
        headerWrapper.style.zIndex = props.headerStickyZIndex;
        headerWrapper.style.top = props.headerStickyTop;
        headerWrapper.style.maxWidth = `${tableWidth.value}px`;
      }
    } else if (headerWrapper && headerWrapper.style.position === 'fixed' && (tableIsIntersectingHeight.value > headerWrapper.offsetHeight * 2)) {
      headerWrapper.style.position = '';
      headerWrapper.style.zIndex = '';
      headerWrapper.style.top = '';
      headerWrapper.style.maxWidth = '100%';
    }

    // 底部滚动条吸底逻辑
    const rollerWrapper = devElTableRef.value?.$el.querySelector('.el-scrollbar__thumb');
    if (!bottomIsIntersecting.value && props.openRollerSticky && rollerWrapper) {
      if (rollerWrapper.style.position !== 'fixed') { // 仅在状态变化时修改样式
        rollerWrapper.style.position = 'fixed';
        rollerWrapper.style.bottom = props.rollerStickyBottom;
      }
    } else if (rollerWrapper && rollerWrapper.style.position === 'fixed') {
      rollerWrapper.style.position = '';
      rollerWrapper.style.bottom = '';
    }
  } else {
    // 当表格整体不可见时重置样式
    resetStyles();
  }
}
const observer = ref<IntersectionObserver | null>(null);
/**
 * 获取指定元素信息
 */
const getDomInfo = debounce(() => {
  if (observer.value) observer.value.disconnect();// 清理旧实例
  observer.value = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      const id = entry.target.getAttribute('data-id') as 'anchorPointRef' | 'devElTableRef' | 'rollerAnchorPoint';
      switch (id) {
        case "anchorPointRef":
          headerIsIntersecting.value = entry.isIntersecting;
          break;
        case "devElTableRef":
          tableIsIntersecting.value = entry.isIntersecting;
          tableIsIntersectingHeight.value = entry.intersectionRect.height;
          break;
        case "rollerAnchorPoint":
          bottomIsIntersecting.value = entry.isIntersecting;
          break;
      }
    });
    setStyles();
  }, {threshold: [0, 0.1, 0.2, 0.3, 0.4, 1]});
  // 表头
  if (anchorPointRef.value && props.openHeaderSticky) {
    observer.value.observe(anchorPointRef.value);
  }
  // 底部滚动条
  if (rollerAnchorPoint.value && props.openRollerSticky) {
    observer.value.observe(rollerAnchorPoint.value);
  }
  // table表格
  if(devElTableRef.value && (props.openHeaderSticky || props.openRollerSticky)){
    observer.value.observe(devElTableRef.value.$el);
  }
}, 200);

// 强制修改table中的数据
const forceUpdateTableData = (data: object[]) => {
  tableData.value = data;
  // 如果开启了分页把总条数重置为0
  if(props.isPagination){
    resetTotalNum();
  }
  nextTick(() => {
    isOpenSortable();
  });
};

// 重置total
const resetTotalNum = () => {
  totalNum.value = 0;
};


onMounted(() => {
  if(props.defaultRequest && props.openRequest){
    nextTick(() => {
      getTableList();
    });
  }
});
onUnmounted(() => {
  if (observer.value) {
    observer.value.disconnect();
  }
})
// 监听数据变化
watch(() => props.data, (value: object[]) => {
  if (!props.openRequest){
    tableData.value = props.data;
    nextTick(()=>{
      isOpenSortable();
    })
  }
}, {deep: true, immediate: true})

// 向外抛出可用的方法
defineExpose({
  /**
   * DevSearchTable组件中获取列表数据
   */
  getTableList,
  /**
   * DevSearchTable组件中重置列表数据
   */
  resetTableList,
  /**
   * DevSearchTable组件开启勾选功能（openSelection）后，主动清除已勾选内容
   */
  clearAllSelection,
  /**
   * DevSearchTable组件改变每页条数
   */
  sizeChange,
  /**
   * DevSearchTable组件改变页码
   */
  currentChange,
  /**
   * DevSearchTable组件强制修改table中的数据
   */
  forceUpdateTableData,
  /**
   * DevSearchTable组件重置total
   */
  resetTotalNum,
  devElTableRef
});

defineOptions({
  name: 'DevSearchTable',
  inheritAttrs: false
})

</script>

<template>
  <div class="dev-search-table-box">
    <el-card :style="props.style">
      <slot name="table-top"></slot>
      <div data-id="anchorPointRef" ref="anchorPointRef"/>
      <el-table
          v-loading="loading"
          data-id="devElTableRef"
          ref="devElTableRef"
          class="table-top-dom"
          :style="tableStyle"
          v-bind="defaultTableProps"
          :data="tableData">
        <el-table-column
            v-if="props.openSelection"
            :selectable="props.selectable"
            align="center"
            type="selection"
            fixed="left"
            width="55"/>
        <el-table-column type="index" v-if="props.openIndex" v-bind="props.elTableColumnIndexAttrs">
          <template #default="item">
            <span v-if="props.isPagination">{{ item.$index + 1 + (currentPage - 1) * pageSize }}</span>
            <span v-else>{{ item.$index + 1 }}</span>
          </template>
        </el-table-column>
        <template v-for="val in props.columns">
          <dev-table-column :item="val" :emptyText="props.emptyText">
            <!-- 插槽透传 -->
            <template v-for="itemSlot in Object.keys(slots)" :key="itemSlot" v-slot:[itemSlot]="temp">
              <slot :name="itemSlot" v-bind="temp"></slot>
            </template>
          </dev-table-column>
        </template>
      </el-table>
      <div data-id="rollerAnchorPoint" ref="rollerAnchorPoint"/>

      <div v-if="totalNum > 0 && props.isPagination" class="dev-page-jump">
        <el-pagination
            v-model:current-page="currentPage"
            v-model:page-size="pageSize"
            :total="totalNum"
            v-bind="defaultPaginationProps"
            @size-change="sizeChange"
            @current-change="currentChange"/>
      </div>
    </el-card>

    <div class="dev-table-scrollbar-box">
      <div class="scrollbar-div"></div>
    </div>
  </div>
</template>

<style lang="scss">
.dev-search-table-box{
  position: relative;
  .dev-page-jump {
    display: flex;
    justify-content: flex-end;
    margin-top: 20px;
  }

  .is-vertical {
    .el-scrollbar__thumb {
      background-color: transparent;
    }
  }

  .el-scrollbar__bar.is-horizontal > div {
    height: 8px;
  }
}

</style>
